package org.ljy;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * HomoSet 测试数据可视化（从指定路径读文档 + 适配 jcommon 1.0.24）
 */
public class HomoTestReportAnlysis {

    // 存储单个homoSet的关键数据
    static class HomoSetData {
        int totalSeqCount;     // 同源集合中包含的序列对象总数（横坐标）
        int newPathCount;      // 覆盖新路径的序列数（图1纵坐标）
        int bugTriggerCount;   // 触发Bug的序列数（图2纵坐标）

        public HomoSetData(int totalSeqCount, int newPathCount, int bugTriggerCount) {
            this.totalSeqCount = totalSeqCount;
            this.newPathCount = newPathCount;
            this.bugTriggerCount = bugTriggerCount;
        }
    }

    // 文档路径（你指定的路径）
    private static final String REPORT_PATH = "/home/losewinner/crashfuzz/crashfuzz-outputs-0829-48h/HOMO_INFO_REPORT-0829-48h.txt";

    public static void main(String[] args) {
        try {
            // 1. 读取文档内容
            String reportContent = readFileToString(REPORT_PATH);
            // 2. 提取有效数据（过滤：触发Bug=0 或 新路径=0 的集合）
            List<HomoSetData> validDataList = extractValidHomoSetData(reportContent);

            // 3. 生成两张目标图表（Swing主线程安全显示）
            SwingUtilities.invokeLater(() -> {
                createChartWindow(
                        "同源集合总序列数 vs 覆盖新路径序列数",
                        "同源集合中包含的序列对象数量",  // 横坐标标签
                        "覆盖了代码新执行路径的序列总数", // 纵坐标标签
                        createNewPathDataset(validDataList)
                );

                createChartWindow(
                        "同源集合总序列数 vs 触发Bug序列数",
                        "同源集合中包含的序列对象数量",  // 横坐标标签
                        "触发了bug的序列总数",          // 纵坐标标签
                        createBugTriggerDataset(validDataList)
                );
            });

        } catch (IOException e) {
            // 处理文件读取异常（如路径错误、权限不足）
            System.err.println("文件读取失败！请检查路径和权限：" + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 从指定路径读取文件，转为完整字符串（保留换行符，确保正则匹配）
     */
    private static String readFileToString(String filePath) throws IOException {
        StringBuilder content = new StringBuilder();
        // try-with-resources 自动关闭流，避免资源泄漏
        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = br.readLine()) != null) {
                content.append(line).append("\n");
            }
        }
        return content.toString();
    }

    /**
     * 提取有效数据：
     * - 匹配“同源集合总序列数、新路径序列数、触发Bug序列数”
     * - 过滤规则：触发Bug序列数>0 且 覆盖新路径序列数>0（两类均非0才保留）
     */
    private static List<HomoSetData> extractValidHomoSetData(String reportContent) {
        List<HomoSetData> dataList = new ArrayList<>();

        // 正则表达式：精准匹配每个homoSet的3个关键字段
        Pattern pattern = Pattern.compile(
                "The \\d+th homoSet:.*?" +                          // 匹配homoSet标识
                        "该同源集合中总共： (\\d+) 个序列对象.*?" +          // 1. 同源集合总序列数（横坐标）
                        "覆盖了代码新执行路径的序列总共： (\\d+) 个.*?" +     // 2. 覆盖新路径序列数
                        "触发了bug的序列总共： (\\d+) 个",                  // 3. 触发Bug序列数
                Pattern.DOTALL | Pattern.MULTILINE  // DOTALL：.匹配换行；MULTILINE：^$匹配行首行尾
        );
        Matcher matcher = pattern.matcher(reportContent);

        // 遍历所有匹配结果，应用过滤规则
        while (matcher.find()) {
            int totalSeq = Integer.parseInt(matcher.group(1));  // 同源集合总序列数
            int newPath = Integer.parseInt(matcher.group(2));   // 覆盖新路径序列数
            int bug = Integer.parseInt(matcher.group(3));       // 触发Bug序列数

            // 核心过滤：仅保留“新路径>0 且 Bug>0”的集合
            if (newPath > 0 && bug > 0) {
                dataList.add(new HomoSetData(totalSeq, newPath, bug));
            }
        }

        // 打印有效数据统计（方便验证）
        System.out.println("符合条件的同源集合数量：" + dataList.size());
        System.out.println("（条件：触发Bug序列数>0 且 覆盖新路径序列数>0）");
        return dataList;
    }

    /**
     * 构建“同源集合总序列数 - 覆盖新路径序列数”数据集（图1用）
     */
    private static XYDataset createNewPathDataset(List<HomoSetData> dataList) {
        XYSeries series = new XYSeries("有效同源集合");  // 序列名称（显示在图例）
        for (HomoSetData data : dataList) {
            // 横坐标：同源集合总序列数；纵坐标：覆盖新路径序列数
            series.add(data.totalSeqCount, data.newPathCount);
        }
        return new XYSeriesCollection(series);  // 包装为JFreeChart可识别的数据集
    }

    /**
     * 构建“同源集合总序列数 - 触发Bug序列数”数据集（图2用）
     */
    private static XYDataset createBugTriggerDataset(List<HomoSetData> dataList) {
        XYSeries series = new XYSeries("有效同源集合");  // 序列名称（显示在图例）
        for (HomoSetData data : dataList) {
            // 横坐标：同源集合总序列数；纵坐标：触发Bug序列数
            series.add(data.totalSeqCount, data.bugTriggerCount);
        }
        return new XYSeriesCollection(series);  // 包装为JFreeChart可识别的数据集
    }

    /**
     * 创建图表窗口并显示（适配jcommon 1.0.24低版本API）
     */
    private static void createChartWindow(String windowTitle, String xLabel, String yLabel, XYDataset dataset) {
        // 1. 创建散点图（旧版本API固定参数，确保兼容性）
        JFreeChart chart = ChartFactory.createScatterPlot(
                windowTitle,          // 图表标题
                xLabel,               // 横坐标标签
                yLabel,               // 纵坐标标签
                dataset,              // 数据集
                PlotOrientation.VERTICAL,  // 垂直布局（常规上下分布）
                true,                 // 显示图例（区分序列）
                true,                 // 显示工具提示（鼠标悬停看坐标）
                false                 // 不显示URL链接（无用功能关闭）
        );

        // 2. 配置图表样式（适配低版本渲染器，确保数据点清晰）
        XYPlot plot = (XYPlot) chart.getPlot();
        // 强制转换为XYLineAndShapeRenderer（旧版本散点图专用渲染器）
        XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
        renderer.setSeriesPaint(0, new Color(255, 69, 0));  // 数据点颜色（橙色，醒目）
        renderer.setSeriesShape(0, new Rectangle(8, 8));    // 数据点大小（8x8像素，易观察）
        //renderer.setDrawLines(false);  // 关闭序列连线（散点图无需连线，避免杂乱）

        // 3. 优化背景和网格线（提升可读性）
        plot.setBackgroundPaint(Color.WHITE);          // 图表背景（白色，简洁）
        plot.setDomainGridlinePaint(Color.LIGHT_GRAY); // 横坐标网格线（浅灰，辅助定位）
        plot.setRangeGridlinePaint(Color.LIGHT_GRAY);  // 纵坐标网格线（浅灰，辅助定位）

        // 4. 创建窗口并显示图表
        JFrame frame = new JFrame(windowTitle);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);  // 关闭窗口不退出程序（多窗口独立）
        frame.setSize(900, 600);  // 窗口大小（宽900，高600，适配数据范围）
        frame.add(new ChartPanel(chart));  // 将图表面板添加到窗口
        frame.setLocationRelativeTo(null);  // 窗口居中显示（用户体验更好）
        frame.setVisible(true);  // 显示窗口
    }
}
